# -*- coding:utf-8 -*-

from scrapy import signals
from .request import SeleniumRequest
from importlib import import_module
from scrapy.http import HtmlResponse
from scrapy.exceptions import NotConfigured
from selenium.webdriver.support.ui import WebDriverWait
from selenium.webdriver.common.desired_capabilities import DesiredCapabilities



class SeleniumMiddleware:
    """Scrapy middleware handling the requests using selenium
    """
    def __init__(self, driver_name, driver_executable_path,
        browser_executable_path, command_executor, driver_arguments, desired_capabilities) -> None:
        """Initialize the selenium webdriver

        Args:
            driver_name (str): The selenium ``WebDriver`` to use.
            driver_executable_path (str): The path of hte executable binary of the driver.
            browser_executable_path (str): The path of the executable binary of the browser.
            command_executor (str): Selenium remote server endpoint
            driver_arguments (list): A list of arguments to initialize the driver.
            desired_capablities (dict) : A dict of cpabilites to initialize the driver.
        """
        webdriver_base_path = f'selenium.webdriver.{driver_name}'

        driver_class_module = import_module(f'{webdriver_base_path}.webdriver')
        driver_class = getattr(driver_class_module, 'WebDriver')

        driver_options_module = import_module(f'{webdriver_base_path}.options')
        driver_options_class = getattr(driver_options_module, 'Options')

        driver_options = driver_options_class()

        if browser_executable_path:
            driver_options.binary_location = browser_executable_path
        for argument in driver_arguments:
            driver_options.add_argument(argument)
        
        # set capbalities.
        desired_capablities_object = DesiredCapabilities()
        driver_capablities = getattr(desired_capablities_object, driver_name.upper())
        for cap in desired_capabilities.keys():
            driver_capablities[cap] = desired_capabilities[cap]

        driver_kwargs = {
            'executable_path': driver_executable_path,
            f'{driver_name}_options': driver_options
        }

        # locally installed driver
        if driver_executable_path is not None:
            driver_kwargs = {
                'desired_capabilities': driver_capablities,
                'executable_path': driver_executable_path,
                f'{driver_name}_options': driver_options
            }
            self.driver = driver_class(**driver_kwargs)

        # remote driver
        elif command_executor is not None:
            from selenium import webdriver
            capabilities = driver_options.to_capabilities()
            self.driver = webdriver.Remote(command_executor=command_executor,
                desired_capabilities=capabilities)

    @classmethod
    def from_crawler(cls, crawler):

        driver_name = crawler.settings.get('SELENIUM_DRIVER_NAME')
        driver_executable_path = crawler.settings.get('SELENIUM_DRIVER_EXECUTABLE_PATH')
        browser_executable_path = crawler.settings.get('SELENIUM_BROWSER_EXECUTABLE_PATH')
        command_executor = crawler.settings.get('SELENIUM_COMMAND_EXECUTOR')
        driver_arguments = crawler.settings.get('SELENIUM_DRIVER_ARGUMENTS')
        desired_capabilities = crawler.settings.get('SELENIUM_DRIVER_DESIRED_CAPABILITIES')

        if driver_name is None:
            raise NotConfigured('SELENIUM_DRIVER_NAME must be set')

        if driver_executable_path is None and command_executor is None:
            raise NotConfigured('Either SELENIUM_DRIVER_EXECUTABLE_PATH'
                'or SELENIUM_COMMAND_EXECUTOR must be set')

        middleware = cls(
            driver_name=driver_name,
            driver_executable_path=driver_executable_path,
            browser_executable_path=browser_executable_path,
            command_executor=command_executor,
            driver_arguments=driver_arguments,
            desired_capabilities=desired_capabilities
        )

        crawler.signals.connect(middleware.spider_closed, signals.spider_idle)

        return middleware

    def process_request(self, request, spider):
        if not isinstance(request, SeleniumRequest):
            return None
        
        self.driver.get(request.url)

        for cookie_name, cookie_value in request.cookies.items():
            self.driver.add_cookie(
                {
                    'name': cookie_name,
                    'value': cookie_value
                }
            )
        
        if request.wait_until:
            WebDriverWait(self.driver, request.wait_time).until(
                request.wait_until
            )

        if request.screenshot:
            request.meta['screenshot'] = self.driver.get_screenshot_as_png()

        if request.script:
            self.driver.execute_script(request.script)

        body = str.encode(self.driver.page_source)

        # Expose the driver via the "meta" attribute
        request.meta.update({'driver': self.driver})

        return HtmlResponse(
            self.driver.current_url,
            body=body,
            encoding='utf-8',
            request=request
        )
    
    def spider_closed(self):
        self.driver.quit()